package biz

import (
	"context"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/gogf/gf/util/gconv"
	jsoniter "github.com/json-iterator/go"
	clientv3 "go.etcd.io/etcd/client/v3"
	"kratosEtcdWatcher/internal/utils/etcd_watch_origin"
	"sync"
)

const (
	LotteryKeyPrefix = "/lottery"
)

var (
	// 全局的存储lottery信息的变量 Notice 也可以用 sync.Map
	//globalLotteryOrigin atomic.Value
	globalLotteryOriginMap sync.Map
)

type GlobalLotteryInterfaceOrigin interface {
	// 获取所有活动的lottery数据
	//GetAllActivitiesLotteriesOrigin(ctx context.Context, fn func(context.Context) ([]Lottery, error)) (map[uint32]Lottery, error)
	// etcd watch，内部有context的管理
	WatchUpdateLottery() error

	GetLotteryByActivityIdFromSyncMap(ctx context.Context, actId uint32, fn func(ctx context.Context, actId uint32) (*Lottery, error)) (*Lottery, error)
}

type EtcdWatchCustomOrigin struct {
	watcher *etcd_watch_origin.Watcher
	logger  *log.Helper
}

func NewEtcdWatchCustomOrigin(client *clientv3.Client, logger log.Logger) (GlobalLotteryInterfaceOrigin, func()) {
	ctx, cancel := context.WithCancel(context.Background())
	watcher := etcd_watch_origin.NewWatcher(client, etcd_watch_origin.WithContext(ctx))
	e := &EtcdWatchCustomOrigin{
		watcher: watcher,
		logger:  log.NewHelper(logger, log.WithMessageKey("EtcdWatchCustomOrigin")),
	}

	// Notice 启动的时候 开一个协程去 watch
	// Notice ———— 用的时候记得打开！！！！！
	//go func() {
	//	err := e.WatchUpdateLottery()
	//	if err != nil {
	//		e.logger.Errorf("NewEtcdWatchCustomOrigin时updateLottery出错了! err: %v", err)
	//	}
	//}()

	return e, func() {
		cancel()
	}
}

// watch etcd 中的 /lottery 这个key，只要有了变动就会触发里面的方法修改本地缓存
func (e *EtcdWatchCustomOrigin) WatchUpdateLottery() error {
	e.logger.Infof("begin watching key %v", LotteryKeyPrefix)

	// 内部有自己的context，当程序结束的时候在代码内部 cancel了，并且内部有 select 捕获ctx.Done()
	err := e.watcher.Watch(LotteryKeyPrefix, func(data *etcd_watch_origin.Data) {

		e.logger.Infof("监听到变化了: key: %v, value: %v", data.Key, data.Value)
		//lotteryItem := &Lottery{}
		//err := jsoniter.UnmarshalFromString(data.Value, &lotteryItem)
		//if err != nil {
		//	e.logger.Errorf("json反序列化data.Value失败了! err: %v", err)
		//	return
		//}
		globalLotteryOriginMap.Store(data.Key, data.Value)

		// Notice 监视前缀、想知道改变前的值
	}, clientv3.WithPrefix(), clientv3.WithPrevKV())

	return err
}

func walk(key, value interface{}) bool {
	fmt.Println("key: ", key, "value: ", value)
	return true
}

// Notice 实际中从本地内存获取lottery的方法
func (e *EtcdWatchCustomOrigin) GetLotteryByActivityIdFromSyncMap(ctx context.Context, actId uint32, fn func(ctx context.Context, actId uint32) (*Lottery, error)) (*Lottery, error) {

	// etcd 的 key
	cacheKey := LotteryKeyPrefix + "/" + gconv.String(actId)
	fmt.Println("内存中的值globalLotteryOriginMap： ")
	globalLotteryOriginMap.Range(walk)

	// 先尝试从内存获取
	lotteryItem, ok := globalLotteryOriginMap.Load(cacheKey)
	if ok {
		fmt.Println("从内存中获取到了数据！！！！！！！")
		lotteryItemModel := &Lottery{}
		err := jsoniter.UnmarshalFromString(gconv.String(lotteryItem), &lotteryItemModel)
		if err != nil {
			e.logger.Errorf("json反序列化data.Value失败了! err: %v", err)
			return nil, err
		}
		return lotteryItemModel, nil
	}

	// 内存中没有 从数据库或Redis获取
	lotteryModel, errModel := fn(ctx, actId)
	if errModel != nil {
		return nil, errModel
	}

	// 更新到本地内存
	globalLotteryOriginMap.Store(cacheKey, gconv.String(lotteryModel))

	return lotteryModel, nil
}

// Notice 实际中获取本地内存中lotter数据的方法: 旧的使用 atomic.Value 存储
/*
func (e *EtcdWatchCustomOrigin) GetAllActivitiesLotteriesOrigin(ctx context.Context, fn func(context.Context) ([]Lottery, error)) (map[uint32]Lottery, error) {
	// Load
	val := globalLotteryOrigin.Load()

	// 本地缓存中没有数据
	if val == nil {
		if fn == nil {
			return nil, errors.New("mission func to get lotteries")
		}
		// Notice 调用传来的函数 从 DB或Redis 中获取lotteries
		lotteries, err := fn(ctx)
		retMap := make(map[uint32]Lottery, 0)
		if err != nil {
			return nil, err
		}
		if len(lotteries) > 0 {
			//groupLotteriesMap := slice.GroupWith(lotteries, func(item Lottery) uint32 {
			//	return item.ActivityId
			//})
			for _, item := range lotteries {
				retMap[item.ActivityId] = item
			}
			// Store
			globalLotteryOrigin.Store(retMap)
			return retMap, nil
		}
	}

	// 本地缓存中有数据
	lotteriesMap, ok := val.(map[uint32]Lottery)
	if !ok {
		return nil, errors.New("no lotteries cache!")
	}

	return lotteriesMap, nil
}
*/
